package com.ray.tool.file;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.ray.tool.RayConsole;

import java.io.*;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;

/**
 * 描述 TODO 关于文件记得搬运
 *
 * @author LiaoYuXing-Ray
 * @version 1.0
 * @createDate 2023/8/29 15:13
 **/
@SuppressWarnings("unused")
public class RayFileTools {

    /**
     * 工具类中的方法都是静态方法访问的，因此将构造器私有不允许创建对象
     *
     * @author LiaoYuXing-Ray 2023/9/7 10:28
     */
    private RayFileTools() {
        throw new AssertionError();
    }

    /**
     * 文件头标识 2023/9/7 9:49
     * RayHeader
     */
    public static final byte[] header = new byte[]{82, 97, 121, 72, 101, 97, 100, 101, 114};
    /**
     * 文件尾标识 2023/9/7 9:49
     * RayTail
     */
    public static final byte[] tail = new byte[]{82, 97, 121, 84, 97, 105, 108};
    /**
     * 帧长度 2023/9/7 9:49
     */
    public static final int frameLength = 14;


    /**
     * 判断两个对象数组是否相等 - 私有
     *
     * @param array1 第一个对象数组
     * @param array2 第二个对象数组
     * @return 如果两个对象数组相等，返回true；否则返回false
     * @author LiaoYuXing-Ray 2023/9/7 10:15
     */
    private static boolean isEquals(Object array1, Object array2) {
        if (array1 == null || array2 == null) {
            return array1 == array2;
        }
        if (!array1.getClass().isArray() || !array2.getClass().isArray()) {
            return false;
        }
        if (array1.getClass() != array2.getClass()) {
            return false;
        }
        int length = java.lang.reflect.Array.getLength(array1);
        if (length != java.lang.reflect.Array.getLength(array2)) {
            return false;
        }
        for (int i = 0; i < length; i++) {
            Object element1 = java.lang.reflect.Array.get(array1, i);
            Object element2 = java.lang.reflect.Array.get(array2, i);
            if (element1 == null) {
                if (element2 != null) {
                    return false;
                }
            } else if (!element1.equals(element2)) {
                return false;
            }
        }
        return true;
    }


    /**
     * 验证帧头是否符合预期
     *
     * @param data 帧头
     * @return boolean 符合，返回true；否则返回false
     * @author LiaoYuXing-Ray 2023/9/7 10:15
     **/
    public static boolean validHeader(byte[] data) {
        return isEquals(data, header);
    }

    /**
     * 验证帧尾是否符合预期
     *
     * @param data 帧尾
     * @return boolean 符合，返回true；否则返回false
     * @author LiaoYuXing-Ray 2023/9/7 10:15
     **/
    public static boolean validTail(byte[] data) {
        return isEquals(data, tail);
    }

    /**
     * 文件读取 - 通过文件绝对路径读取文件内容
     *
     * @param filePath 通过文件绝对路径
     * @return java.lang.String 返回文件读取的字符串
     * @author LiaoYuXing-Ray 2023/8/31 13:47
     **/
    public static String readFileByFilePath(String filePath) {
        StringBuilder content = new StringBuilder();
        try (BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath))) {
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                content.append(line).append("\n");
            }
        } catch (Exception e) {
            RayConsole.err("文件读取失败！" + e.getMessage());
        }
        return content.toString();
    }

    /**
     * 用于将多个路径参数拼接成一个完整的路径字符串
     *
     * @param type 路径类型，1表示正常拼接路径，其他值表示特殊处理
     * @param args 可变长度的路径参数数组。
     * @return 返回拼接后的路径字符串。
     * @author LiaoYuXing-Ray 2023/9/7 16:12
     */
    public static String pathCombine(int type, String... args) {
        StringBuilder path = new StringBuilder();
        for (String arg : args) {
            path.append(arg);
            path.append(File.separator);
        }
        String result = path.toString();
        if (type == 1 && result.endsWith(File.separator)) {
            result = result.substring(0, result.length() - 1);
        }
        return result;
    }


    /**
     * 读取指定文件的所有行并返回一个字符串列表
     *
     * @param filename 文件名
     * @return java.util.List<java.lang.String>
     * @author LiaoYuXing-Ray 2023/9/18 13:43
     **/
    public static List<String> readFileAllLines(String filename) {
        try {
            // 使用Files类的readAllLines方法读取指定文件的所有行
            // 将文件名转换为Path对象
            return Files.readAllLines(Paths.get(filename), StandardCharsets.UTF_8);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }


    /**
     * 读取一个文件并将其内容转换为字节数组
     *
     * @param file 文件路径
     * @return 字节数组
     * @author LiaoYuXing-Ray 2023/9/18 13:32
     **/
    public static byte[] readFileBytes(String file) {
        try {
            return Files.readAllBytes(new File(file).toPath());
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 删除文件
     *
     * @param filePath 文件目录
     * @return boolean 是否删除成功
     * @author LiaoYuXing-Ray 2023/9/18 13:41
     **/
    public static boolean deleteFile(String filePath) {
        // 创建一个Path对象，表示要删除的文件或目录
        Path path = Paths.get(filePath);

        // 检查文件是否存在，是否是一个目录，以及是否可以被删除
        // 如果文件存在且是一个目录，则递归地删除目录及其子目录和文件并返回true；否则返回false
        if (Files.exists(path)) {
            if (Files.isDirectory(path)) {
                try {
                    Files.list(path).forEach(subPath ->
                            deleteFile(subPath.toString())
                    );
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            try {
                return Files.deleteIfExists(path);
            } catch (IOException e) {
                e.printStackTrace();
                return false;
            }
        } else {
            return false;
        }
    }


    /**
     * 写文件
     *
     * @param path 文件写入地址，若文件会创建路径，且会直接替换
     * @param data 数据
     * @author LiaoYuXing-Ray 2023/9/18 11:53
     **/
    public static void writeFile(String path, byte[] data) {
        Path filePath = Paths.get(path);
        try (FileOutputStream fos = new FileOutputStream(filePath.toFile())) {
            fos.write(data);
            fos.flush();
            RayConsole.out("保存文件");
        } catch (IOException e) {
            RayConsole.err("保存文件失败", e);
        }
    }

    /**
     * 写文件 - 重载
     *
     * @param path 文件写入地址，若文件会创建路径，且会直接替换
     * @param data 数据
     * @author LiaoYuXing-Ray 2023/9/18 11:53
     **/
    public static void writeFile(String path, String data) {
        if (data == null) {
            data = "";
        }
        writeFile(path, data.getBytes(StandardCharsets.UTF_8));
    }


    /**
     * 将对象t转换为JSON字符串，使用了一些序列化特性来处理日期、空值等
     *
     * @param path 保存路径
     * @param t    对象
     * @author LiaoYuXing-Ray 2023/9/18 13:26
     **/
    public static <T> void saveObjectFileToJson(String path, T t) {
        // 指定了一些序列化特性，例如使用日期格式、处理空值等。转换后的JSON字符串存储在jsonStr变量中
//        String jsonStr = JSONObject.toJSONString(t, SerializerFeature.WriteDateUseDateFormat,
//                SerializerFeature.WriteMapNullValue, SerializerFeature.WriteNullStringAsEmpty,
//                SerializerFeature.WriteNullBooleanAsFalse, SerializerFeature.WriteNullNumberAsZero, SerializerFeature.PrettyFormat);
        Gson gson = new GsonBuilder()
                .setDateFormat("yyyy-MM-dd HH:mm:ss") // 设置日期格式
                .serializeNulls() // 处理空值
                .create();
        String jsonStr = gson.toJson(t); // 使用Gson将对象转换为JSON字符串
        writeFile(path, jsonStr);
    }


    /**
     * 检查指定路径的文件或目录是否存在
     *
     * @param filePath 要检查的文件到绝对路径
     * @return boolean 是否存在
     * @author LiaoYuXing-Ray 2023/9/18 13:59
     **/
    public static boolean fileExists(String filePath) {
        try {
            // 对文件路径进行URL解码，以支持包含特殊字符的路径
            filePath = URLDecoder.decode(filePath, String.valueOf(StandardCharsets.UTF_8));
            // 将解码后的文件路径转换为Path对象
            Path path = Paths.get(filePath);
            // 检查Path对象对应的文件是否存在，存在则返回true，不存在则返回false
            return Files.exists(path);
        } catch (Exception ex) {
            ex.printStackTrace();
            // 如果在try块中发生任何异常，catch块会捕获这个异常，并返回false，表示文件不存在
            return false;
        }
    }

    /**
     * 创建该路径对应的目录 - 只是创建目录不是创建文件
     *
     * @param dirPath 文件路径
     * @return boolean
     * @author LiaoYuXing-Ray 2023/9/18 14:05
     **/
    public static boolean createDirectory(String dirPath) {
        try {
            // 将传入的文件路径进行URL解码，以处理可能存在的特殊字符
            dirPath = URLDecoder.decode(dirPath, StandardCharsets.UTF_8.name());
            Path path = Paths.get(dirPath);
            // 如果目录已经存在，或者成功创建了目录，则返回true
            return Files.exists(path) || Files.createDirectories(path).toFile().exists();
        } catch (Exception ex) {
            ex.printStackTrace();
            // 如果出现异常，则返回false
            return false;
        }
    }

    /**
     * 读取指定路径的文件内容
     * 如果文件不存在或者在读取过程中发生错误，将返回空字符串。
     *
     * @param filePath 文件的路径
     * @return java.lang.String
     * @author LiaoYuXing-Ray 2023/9/18 14:36
     **/
    public static String readFile(String filePath) {
        if (fileExists(filePath)) {
            // 使用Paths.get()方法获取文件路径，并赋值给变量path
            Path path = Paths.get(filePath);
            try (BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
                // 创建一个StringBuilder对象，用于存储文件内容
                StringBuilder content = new StringBuilder();
                // 声明一个字符串变量line，用于存储每次读取的一行内容
                String line;
                // 当读取的行不为空时，将该行内容添加到content中，并在末尾添加换行符
                while ((line = reader.readLine()) != null) {
                    content.append(line).append("\n");
                }
                // 将content转换为字符串并返回
                return content.toString();
            } catch (IOException ex) {
                RayConsole.error("读取文件失败：" + filePath, ex);
                return "";
            }
        } else {
            return "";
        }
    }

}
